﻿// --- Copyright (c) 2010 Steven Hartgers ---
// Copyright (C) 2010 Steven Hartgers All rights are reserved.
// Author contact: fiinix00/at\gmail.com
// ------------------------------------------------------------------------
// This program is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more detail
//  
// You should have received a copy of the GNU General Public License 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Hack
{
    /// <summary>
    /// http://bartdesmet.net/blogs/bart/archive/2009/08/30/type-free-lambda-calculus-in-c-pre-4-0-defining-the-lambda-language-runtime-llr.aspx
    /// </summary>
    public static class ExtremeLamba
    {
        public delegate void Action();

        public class Exception<T> : Exception
        {
            public Exception(T data)
            {
                Data = data;
            }

            public new T Data { get; private set; }
        }

        public static Action λ(Func<Action, Action> x)
        {
            return () => { throw new Exception<Func<Action, Action>>(x); };
        }
        public static Action ß(Action x, Action y)
        {
            Func<Action, Action> xe;

            try
            {
                x();
                xe = _ => _;
            }
            catch (Exception<Func<Action, Action>> ex)
            {
                xe = ex.Data;
            }

            return xe(y);
        }


        public static Func<Action, bool> toBool = b =>
        {
            bool res = false;
            ß(ß(b, () => { res = true; }), () => { res = false; })();
            return res;
        };
        public static Func<bool, Action> fromBool = b => b ? T : F;

        
        //
        // Church Booleans
        //
        public static Action T = λ(a => λ(b => a)); // \ab.a
        public static Action F = λ(a => λ(b => b)); // \ab.b

        public static Action Not = λ(m => λ(a => λ(b => ß(ß(m, b), a)))); // \m.\ab.mba
        public static Action And = λ(m => λ(n => ß(ß(m, n), m))); // \mn.mnm
        public static Action Or = λ(m => λ(n => ß(ß(m, m), n))); // \mn.mmn
        public static Action Xor = λ(m => λ(n => λ(a => λ(b => ß(ß(m, ß(ß(n, b), a)), ß(ß(n, a), b)))))); // \mn.\ab.m(nba)(nab)

        public static Action Zero = λ(n => ß(ß(n, λ(x => F)), T)); // \n.n(\x.F)T
        public static Action Succ = λ(n => λ(f => λ(x => ß(f, ß(ß(n, f), x))))); // \n.\fx.f(nfx)
        public static Action Pred = λ(n => λ(f => λ(x => ß(ß(ß(n, λ(g => λ(h => ß(h, ß(g, f))))), λ(u => x)), λ(u => u)))));
        public static Action Plus = λ(m => λ(n => λ(f => λ(x => ß(ß(m, f), ß(ß(n, f), x)))))); // \mn.\fx.mf(nfx)
        public static Action Sub = λ(m => λ(n => ß(ß(n, Pred), m)));
        public static Action Mul = λ(m => λ(n => λ(f => ß(n, ß(m, f))))); // \mn.\f.n(mf)
        public static Action Exp = λ(m => λ(n => ß(n, m))); // \mn.nm
        
        public static Func<Action, Action> zero = toFunc1(Zero);
        public static Func<Action, Action> succ = toFunc1(Succ);
        public static Func<Action, Action> pred = toFunc1(Pred);
        public static Func<Action, Action, Action> plus = toFunc2(Plus);
        public static Func<Action, Action, Action> sub = toFunc2(Sub);
        public static Func<Action, Action, Action> mul = toFunc2(Mul);
        public static Func<Action, Action, Action> exp = toFunc2(Exp);

        //
        // Conversion operations
        //
        public static Func<Action, Func<Action, Action>> toFunc1 = f => x => ß(f, x);
        public static Func<Action, Func<Action, Action, Action>> toFunc2 = f => (x, y) => ß(ß(f, x), y);
        public static Func<Action, Func<Action, Action, Action, Action>> toFunc3 = f => (x, y, z) => ß(ß(ß(f, x), y), z);
        public static Func<Func<Action, Action>, Action> fromFunc1 = f => λ(x => f(x));
        public static Func<Func<Action, Action, Action>, Action> fromFunc2 = f => λ(x => λ(y => f(x, y)));
        public static Func<Func<Action, Action, Action, Action>, Action> fromFunc3 = f => λ(x => λ(y => λ(z => f(x, y, z))));



    
    }
}
